home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / progjrn / pj_vga.arc / TIMER.ASM < prev    next >
Assembly Source File  |  1989-06-14  |  11KB  |  441 lines

  1. ;
  2. ; *** Listing 1 ***
  3. ;
  4. ; *******************************************************
  5. ; * From The Zen of Assembler. Appears by permission of *
  6. ; * Scott, Foresman & Company.                          *
  7. ; *******************************************************
  8. ;
  9. ; Uses the 8253 timer to time the performance of code that takes
  10. ; less than about 54 milliseconds to execute, with a resolution
  11. ; of better than 10 microseconds.
  12. ;
  13. ; By Michael Abrash 4/14/89
  14. ;
  15. ; Externally callable routines:
  16. ;
  17. ;  TimerOn: Starts the timer, with interrupts disabled.
  18. ;
  19. ;  TimerOff: Stops the timer, saves the timer count,
  20. ;    times the overhead code, and restores interrupts to the
  21. ;    state they were in when TimerOn was called.
  22. ;
  23. ;  TimerReport: Prints the net time that passed between starting
  24. ;    and stopping the timer.
  25. ;
  26. ; Note: If more than about 54 ms passes between TimerOn and
  27. ;    TimerOff calls, the timer turns over and the count is
  28. ;    inaccurate. When this happens, an error message is displayed
  29. ;    instead of a count.
  30. ;
  31. ; Note: Interrupts *MUST* be left off between calls to TimerOn
  32. ;    and TimerOff for accurate timing and for detection of
  33. ;    timer overflow.
  34. ;
  35. ; Note: These routines can introduce slight inaccuracies into the
  36. ;    system clock count for each code section timed even if
  37. ;    timer 0 doesn't overflow. If timer 0 does overflow, the
  38. ;    system clock can become slow by virtually any amount of
  39. ;    time, since the system clock can't advance while the
  40. ;    timer is timing. Consequently, it's a good idea to reboot
  41. ;    at the end of a timing session. (The battery-backed clock,
  42. ;    if any, is not affected.)
  43. ;
  44. ; All registers, and all flags except the interrupt flag, are
  45. ; preserved by all routines. Interrupts are enabled and then disabled
  46. ; by TimerOn, and are restored by TimerOff to the state they were
  47. ; in when TimerOn was called.
  48. ;
  49.  
  50. Code    segment word public 'CODE'
  51.     assume    cs:Code, ds:nothing
  52.     public    TimerOn, TimerOff, TimerReport
  53.  
  54. ;
  55. ; Base address of the 8253 timer chip.
  56. ;
  57. BASE_8253        equ    40h
  58. ;
  59. ; The address of the timer 0 count registers in the 8253.
  60. ;
  61. TIMER_0_8253        equ    BASE_8253 + 0
  62. ;
  63. ; The address of the mode register in the 8253.
  64. ;
  65. MODE_8253        equ    BASE_8253 + 3
  66. ;
  67. ; The address of Operation Command Word 3 in the 8259 Programmable
  68. ; Interrupt Controller (PIC) (write only, and writable only when
  69. ; bit 4 of the byte written to this address is 0 and bit 3 is 1).
  70. ;
  71. OCW3            equ    20h
  72. ;
  73. ; The address of the Interrupt Request register in the 8259 PIC
  74. ; (read only, and readable only when bit 1 of OCW3 = 1 and bit 0
  75. ; of OCW3 = 0).
  76. ;
  77. IRR            equ    20h
  78. ;
  79. ; Macro to emulate a POPF instruction while fixing the bug in some
  80. ; 80286 chips which allows interrupts to occur during a POPF even when
  81. ; interrupts remain disabled.
  82. ;
  83. MPOPF macro 
  84.     local    p1, p2
  85.     jmp short p2
  86. p1:    iret            ;jump to pushed address & pop flags
  87. p2:    push    cs        ;construct far return address to
  88.     call    p1        ; the next instruction
  89.     endm
  90.  
  91. ;
  92. ; Macro to delay briefly to ensure that enough time has elapsed
  93. ; between successive I/O accesses so that the device being accessed
  94. ; can respond to both. Jumping flushes the prefetch queue, forcing
  95. ; a memory access.
  96. ;
  97. DELAY    macro
  98.     jmp    $+2
  99.     jmp    $+2
  100.     jmp    $+2
  101.     endm
  102.  
  103. OriginalFlags        dw    ?    ;processor flags in effect
  104.                     ; when TimerOn called
  105. TimedCount        dw    ?    ;timer 0 count when the timer
  106.                     ; is stopped
  107. ReferenceCount        dw    ?    ;number of counts required to
  108.                     ; execute timer overhead code
  109. OverflowFlag        db    ?    ;used to indicate whether the
  110.                     ; timer overflowed during the
  111.                     ; timing interval
  112. ;
  113. ; String printed to report results.
  114. ;
  115. OutputStr    label    byte
  116.         db    0dh, 0ah, 'Timed count: ', 5 dup (?)
  117. ASCIICountEnd    label    byte
  118.         db    ' microseconds', 0dh, 0ah
  119.         db    '$'
  120. ;
  121. ; String printed to report timer overflow.
  122. ;
  123. OverflowStr    label    byte
  124.     db    0dh, 0ah
  125.     db    '****************************************************'
  126.     db    0dh, 0ah
  127.     db    '* The timer overflowed; the interval timed was too *'
  128.     db    0dh, 0ah
  129.     db    '* long for the timer to measure.                   *'
  130.     db    0dh, 0ah
  131.     db    '****************************************************'
  132.     db    0dh, 0ah
  133.     db    '$'
  134.  
  135. ;********************************************************************
  136. ;* Routine called to start timing.                    *
  137. ;********************************************************************
  138.  
  139. TimerOn    proc    near
  140.  
  141. ;
  142. ; Save the context of the program being timed.
  143. ;
  144.     push    ax
  145.     pushf
  146.     pop    ax            ;get flags so we can force
  147.                     ; interrupts off when leaving
  148.                     ; this routine
  149.     mov    cs:[OriginalFlags],ax
  150.     and    ax,0fdffh         ;set pushed interrupt flag
  151.                     ; to 0
  152.     push    ax
  153. ;
  154. ; Turn on interrupts, so the timer interrupt can occur if it's
  155. ; pending.
  156. ;
  157.     sti
  158. ;
  159. ; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
  160. ; linear counting rather than count-by-two counting. Also
  161. ; leaves the 8253 waiting for the initial timer 0 count to
  162. ; be loaded.
  163. ;
  164.     mov    al,00110100b        ;mode 2
  165.     out    MODE_8253,al
  166. ;
  167. ; Set the timer count to 0, so we know we won't get another
  168. ; timer interrupt right away.
  169. ; Note: this introduces an inaccuracy of up to 54 ms in the system
  170. ; clock count each time it is executed.
  171. ;
  172.     DELAY
  173.     sub    al,al
  174.     out    TIMER_0_8253,al        ;lsb
  175.     DELAY
  176.     out    TIMER_0_8253,al        ;msb
  177. ;
  178. ; Wait before clearing interrupts to allow the interrupt generated
  179. ; when switching from mode 3 to mode 2 to be recognized. This delay
  180. ; must be at least 210 ns long to allow time for that interrupt to
  181. ; occur. Here, 10 jumps are used for the delay to ensure that the
  182. ; delay time will be more than long enough even on fast 80386
  183. ; computers.
  184. ;
  185.     rept 10
  186.     jmp    $+2
  187.     endm
  188. ;
  189. ; Disable interrupts to get an accurate count.
  190. ;
  191.     cli
  192. ;
  193. ; Set the timer count to 0 again to start the timing interval.
  194. ;
  195.     mov    al,00110100b        ;set up to load initial
  196.     out    MODE_8253,al        ; timer count
  197.     DELAY
  198.     sub    al,al
  199.     out    TIMER_0_8253,al        ;load count lsb
  200.     DELAY
  201.     out    TIMER_0_8253,al        ;load count msb
  202. ;
  203. ; Restore the context and return.
  204. ;
  205.     MPOPF                ;keeps interrupts off
  206.     pop    ax
  207.     ret
  208.  
  209. TimerOn    endp
  210.  
  211. ;********************************************************************
  212. ;* Routine called to stop timing and get count.                *
  213. ;********************************************************************
  214.  
  215. TimerOff proc    near
  216.  
  217. ;
  218. ; Save the context of the program being timed.
  219. ;
  220.     push    ax
  221.     push    cx
  222.     pushf
  223. ;
  224. ; Latch the count.
  225. ;
  226.     mov    al,00000000b        ;latch timer 0
  227.     out    MODE_8253,al
  228. ;
  229. ; See if the timer has overflowed by checking the 8259 for a pending
  230. ; timer interrupt.
  231. ;
  232.     mov    al,00001010b        ;OCW3, set up to read
  233.     out    OCW3,al            ; Interrupt Request register
  234.     DELAY
  235.     in    al,IRR            ;read Interrupt Request
  236.                     ; register
  237.     and    al,1            ;set AL to 1 if IRQ0 (the
  238.                     ; timer interrupt) is pending
  239.     mov    cs:[OverflowFlag],al    ;yes, it did indeed overflow
  240. ;
  241. ; Allow interrupts to happen again.
  242. ;
  243.     sti
  244. ;
  245. ; Read out the count we latched earlier.
  246. ;
  247.     in    al,TIMER_0_8253        ;least significant byte
  248.     DELAY
  249.     mov    ah,al
  250.     in    al,TIMER_0_8253        ;most significant byte
  251.     xchg    ah,al
  252.     neg    ax            ;convert from countdown
  253.                     ; remaining to elapsed
  254.                     ; count
  255.     mov    cs:[TimedCount],ax
  256. ; Time a zero-length code fragment, to get a reference for how
  257. ; much overhead this routine has. Time it 16 times and average it,
  258. ; for accuracy, rounding the result.
  259. ; Note: REferenceTimerOn resets the timer to 0, which introduces
  260. ; an inaccuracy of up to 54 ms in the system clock count each time
  261. ; it is executed.
  262. ;
  263.     mov    cs:[ReferenceCount],0
  264.     mov    cx,16
  265.     cli                ;interrupts off to allow a
  266.                     ; precise reference count
  267. RefLoop:
  268.     call    ReferenceTimerOn
  269.     call    ReferenceTimerOff
  270.     loop    RefLoop
  271.     sti
  272.     add    cs:[ReferenceCount],8    ;total + (0.5 * 16)
  273.     mov    cl,4
  274.     shr    cs:[ReferenceCount],cl    ;(total) / 16 + 0.5
  275. ;
  276. ; Restore original interrupt state.
  277. ;
  278.     pop    ax            ;retrieve flags when called
  279.     mov    cx,cs:[OriginalFlags]    ;get back the original flags
  280.     and    ch,not 0fdh        ;only care about original
  281.                     ; interrupt flag...
  282.     and    ah,0fdh            ;...keep all other flags in
  283.                     ; their current condition
  284.     or    ah,ch            ;make flags word with original
  285.                     ; interrupt flag
  286.     push    ax            ;prepare flags to be popped
  287. ;
  288. ; Restore the context of the program being timed and return to it.
  289. ;
  290.     MPOPF                ;restore the flags with the
  291.                     ; original interrupt state
  292.     pop    cx
  293.     pop    ax
  294.     ret
  295.  
  296. TimerOff endp
  297.  
  298. ;
  299. ; Called by TimerOff to start timer for overhead measurements.
  300. ;
  301.  
  302. ReferenceTimerOn    proc    near
  303. ;
  304. ; Save the context of the program being timed.
  305. ;
  306.     push    ax
  307.     pushf        ;interrupts are already off
  308. ;
  309. ; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
  310. ; linear counting rather than count-by-two counting.
  311. ;
  312.     mov    al,00110100b    ;set up to load
  313.     out    MODE_8253,al    ; initial timer count
  314.     DELAY
  315. ;
  316. ; Set the timer count to 0.
  317. ;
  318.     sub    al,al
  319.     out    TIMER_0_8253,al    ;load count lsb
  320.     DELAY
  321.     out    TIMER_0_8253,al    ;load count msb
  322. ;
  323. ; Restore the context of the program being timed and return to it.
  324. ;
  325.     MPOPF
  326.     pop    ax
  327.     ret
  328.  
  329. ReferenceTimerOn    endp
  330.  
  331. ;
  332. ; Called by TimerOff to stop timer and add result to ReferenceCount
  333. ; for overhead measurements.
  334. ;
  335.  
  336. ReferenceTimerOff proc    near
  337. ;
  338. ; Save the context of the program being timed.
  339. ;
  340.     push    ax
  341.     push    cx
  342.     pushf
  343. ;
  344. ; Latch the count and read it.
  345. ;
  346.     mov    al,00000000b        ;latch timer 0
  347.     out    MODE_8253,al
  348.     DELAY
  349.     in    al,TIMER_0_8253        ;lsb
  350.     DELAY
  351.     mov    ah,al
  352.     in    al,TIMER_0_8253        ;msb
  353.     xchg    ah,al
  354.     neg    ax            ;convert from countdown
  355.                     ; remaining to amount
  356.                     ; counted down
  357.     add    cs:[ReferenceCount],ax
  358. ;
  359. ; Restore the context of the program being timed and return to it.
  360. ;
  361.     MPOPF
  362.     pop    cx
  363.     pop    ax
  364.     ret
  365.  
  366. ReferenceTimerOff endp
  367.  
  368. ;********************************************************************
  369. ;* Routine called to report timing results.                *
  370. ;********************************************************************
  371.  
  372. TimerReport    proc    near
  373.  
  374.     pushf
  375.     push    ax
  376.     push    bx
  377.     push    cx
  378.     push    dx
  379.     push    si
  380.     push    ds
  381. ;
  382.     push    cs
  383.     pop    ds
  384.     assume    ds:Code
  385. ;
  386. ; Check for timer 0 overflow.
  387. ;
  388.     cmp    [OverflowFlag],0
  389.     jz    PrintGoodCount
  390.     mov    dx,offset OverflowStr
  391.     mov    ah,9
  392.     int    21h
  393.     jmp    short EndTimerReport
  394. ;
  395. ; Convert net count to decimal ASCII in microseconds.
  396. ;
  397. PrintGoodCount:
  398.     mov    ax,[TimedCount]
  399.     sub    ax,[ReferenceCount]
  400.     mov    si,offset ASCIICountEnd - 1
  401. ;
  402. ; Convert count to microseconds by multiplying by .8381.
  403. ;
  404.     mov    dx,8381
  405.     mul    dx
  406.     mov    bx,10000
  407.     div    bx        ;* .8381 = * 8381 / 10000
  408. ;
  409. ; Convert time in microseconds to 5 decimal ASCII digits.
  410. ;
  411.     mov    bx,10
  412.     mov    cx,5
  413. CTSLoop:
  414.     sub    dx,dx
  415.     div    bx
  416.     add    dl,'0'
  417.     mov    [si],dl
  418.     dec    si
  419.     loop    CTSLoop
  420. ;
  421. ; Print the results.
  422. ;
  423.     mov    ah,9
  424.     mov    dx,offset OutputStr
  425.     int    21h
  426. ;
  427. EndTimerReport:
  428.     pop    ds
  429.     pop    si
  430.     pop    dx
  431.     pop    cx
  432.     pop    bx
  433.     pop    ax
  434.     MPOPF
  435.     ret
  436.  
  437. TimerReport    endp
  438.  
  439. Code    ends
  440.     end
  441.